﻿using Bettzueche.Dampfross;
using Bettzueche.DampfrossContinuum.Experimente;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Bettzueche.DampfrossContinuum
{
    /// <summary>
    /// 
    /// </summary>
    class Program
    {

        static void Main(string[] args) {
            Console.WriteLine("Dampfross Continuum");

            /*
             * WARNUNG: Für FeatureCombi 2 - 4 wurde die Hashfunktion in UmgebungC umgeschrieben! Zur reproduktion der älteren Tests
             * muss die alte Hashfunktion wieder genommen werden (ist in UmgebungC auskommentiert)!
             */

            //KonvertiereKarte("hexMap5x6-01");

            //ZustandsnummernSpeichern("hexMap5x6-01c", new HexKoordinate(1, 4), new FeatureCombi1(new LandschaftsTiling()));
            //ZustandsnummernSpeichern("hexMap9x10-01c", new HexKoordinate(3, 8), new FeatureCombi1(new LandschaftsTiling()));
            //ZustandsnummernSpeichern("hexMap9x10-02c", new HexKoordinate(3, 8), new FeatureCombi1(new LandschaftsTiling()));

            //int[][] features = Helper.LadeMerkmale(@"Karten\zIDshexMap5x6-01c.csv");
            //var soll = Helper.LadeSollwerte(@"Karten\Sollwerte9x10-01c.csv");
            //UmgebungC stateX = UmgebungC.GetInstanceFromFeatures(features[0], new LandschaftsTiling());
            //RunTiling1Test();

            //// tilings = 3^1 = 3; precision = r / 3^2 = 0.9/9 = 0.1
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(3, 0.1));
            ////ZustandsnummernSpeichern("hexMap9x10-02c", new HexKoordinate(3, 8), kachelOrganisation);
            //RunTilingXTest(Experiment1.Karten.Map9x10_2_3T, kachelOrganisation.KachelWertUnterteilung);

            //// tilings = 2^0 = 1; precision = r / 2^2 = 0.9/2^2 = 9/40 = 0.225
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.225));
            ////ZustandsnummernSpeichern("hexMap9x10-02c", new HexKoordinate(3, 8), kachelOrganisation);
            //RunTilingXTest(Experiment1.Karten.Map9x10_2_1T4, kachelOrganisation.KachelWertUnterteilung);

            //// tilings = 2^0 = 1; precision = r / 2^4 = 0.9/16 = 9/160 = 0,05625
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.05625));
            ////ZustandsnummernSpeichern("hexMap9x10-02c", new HexKoordinate(3, 8), kachelOrganisation);
            //RunTilingXTest(Experiment1.Karten.Map9x10_2_1T16, kachelOrganisation.KachelWertUnterteilung);

            //// tilings = 2^0 = 1; precision = r / 2^1 = 0.9/2 = 0.45
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.45));
            ////ZustandsnummernSpeichern("hexMap9x10-02c", new HexKoordinate(3, 8), kachelOrganisation);
            //RunTilingXTest(Experiment1.Karten.Map9x10_2_1T2, kachelOrganisation.KachelWertUnterteilung);

            //// tilings = 2^0 = 1; precision = r / 2^0 = 0.9/1 = 0.9
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.9));
            ////ZustandsnummernSpeichern("hexMap9x10-02c", new HexKoordinate(3, 8), kachelOrganisation);
            //RunTilingXTest(Experiment1.Karten.Map9x10_2_1T1, kachelOrganisation.KachelWertUnterteilung);

            //KonvertiereKarte("hexMap14x16-01");

            ////precision = r / 3^2 = 0.9/9 = 0.1
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.1));
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), kachelOrganisation, "zIDshexMap14x16-01c-1T9.csv");
            ////precision = r / 3^1 = 0.9/3 = 0.3
            //kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.3));
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), kachelOrganisation, "zIDshexMap14x16-01c-1T3.csv");
            //// tilings = 2^0 = 1; precision = r / 2^1 = 0.9/2 = 0.45
            //kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.45));
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), kachelOrganisation, "zIDshexMap14x16-01c-1T2.csv");
            //// tilings = 2^0 = 1; precision = r / 2^4 = 0.9/16 = 9/160 = 0,05625
            //kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(1, 0.05625));
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), kachelOrganisation, "zIDshexMap14x16-01c-1T16.csv");

            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-1T9.csv", "Sollwerte14x16-01-1T9.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-1T16.csv", "Sollwerte14x16-01-1T16.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-1T2.csv", "Sollwerte14x16-01-1T2.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-1T3.csv", "Sollwerte14x16-01-1T3.csv");


            //var kachelOrganisation = new LandschaftsTiling(1, 0.45);
            //RunTilingXTest(Experiment1.Karten.Map14x16_1_1T2, kachelOrganisation);
            //kachelOrganisation = new LandschaftsTiling(1, 0.3);
            //RunTilingXTest(Experiment1.Karten.Map14x16_1_1T3, kachelOrganisation);
            //kachelOrganisation = new LandschaftsTiling(1, 0.1);
            //RunTilingXTest(Experiment1.Karten.Map14x16_1_1T9, kachelOrganisation);
            //kachelOrganisation = new LandschaftsTiling(1, 0.05625);
            //RunTilingXTest(Experiment1.Karten.Map14x16_1_1T16, kachelOrganisation);

            //// tilings = 2^1 = 2; precision = r / 2^3 = 0.9/8 = 0.1125 (2*4 = 8 Tiles)
            //var kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(2, 0.1125));
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), kachelOrganisation, "zIDshexMap14x16-01c-2T8.csv");
            //// tilings = 3^1 = 3; precision = r / 3^2 = 0.9/9 = 0.1 (3*3 = 9 Tiles)
            //kachelOrganisation = new FeatureCombi1(new LandschaftsTiling(3, 0.1));
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), kachelOrganisation, "zIDshexMap14x16-01c-3T9.csv");

            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-2T8.csv", "Sollwerte14x16-01c-2T8.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-3T9.csv", "Sollwerte14x16-01c-3T9.csv");

            //var kachelOrganisation = new LandschaftsTiling(2, 0.1125);
            //RunTilingXTest(Experiment1.Karten.Map14x16_1_2T8, kachelOrganisation);
            //kachelOrganisation = new LandschaftsTiling(3, 0.1);
            //RunTilingXTest(Experiment1.Karten.Map14x16_1_3T9, kachelOrganisation);

            /*
             * WARNUNG: Für FeatureCombi 2 - 4 wurde die Hashfunktion in UmgebungC umgeschrieben! Zur reproduktion der älteren Tests
             * muss die alte Hashfunktion wieder genommen werden (ist in UmgebungC auskommentiert)!
             */


            //var kachelOrganisation = new LandschaftsTiling(1, 0.1); // 1 Tiling, 9 Tiles
            //IFeatures<UmgebungC> merkmalsOrganisation = new FeatureCombi2(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-01c-C2.csv");
            //merkmalsOrganisation = new FeatureCombi2b(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-01c-C2b.csv");
            //merkmalsOrganisation = new FeatureCombi3(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-01c-C3.csv");
            //merkmalsOrganisation = new FeatureCombi3b(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-01c-C3b.csv");
            //merkmalsOrganisation = new FeatureCombi4(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-01c-C4.csv");
            //merkmalsOrganisation = new FeatureCombi4b(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-01c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-01c-C4b.csv");

            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-C2.csv", "Sollwerte14x16-01c-C2.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-C2b.csv", "Sollwerte14x16-01c-C2b.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-C3.csv", "Sollwerte14x16-01c-C3.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-C3b.csv", "Sollwerte14x16-01c-C3b.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-C4.csv", "Sollwerte14x16-01c-C4.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-01.csv", @"Karten\zIDshexMap14x16-01c-C4b.csv", "Sollwerte14x16-01c-C4b.csv");

            var kachelOrganisation = new LandschaftsTiling(1, 0.1); // 1 Tiling, 9 Tiles
            IFeatures<UmgebungC> merkmalsOrganisation = new FeatureCombi2(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_1_C2, merkmalsOrganisation);

            merkmalsOrganisation = new FeatureCombi2b(kachelOrganisation);
            RunTilingZTest(Experiment1.Karten.Map14x16_1_C2b, merkmalsOrganisation);

            //merkmalsOrganisation = new FeatureCombi3(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_1_C3, merkmalsOrganisation);

            //merkmalsOrganisation = new FeatureCombi3b(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_1_C3b, merkmalsOrganisation);

            //merkmalsOrganisation = new FeatureCombi4(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_1_C4, merkmalsOrganisation);

            //merkmalsOrganisation = new FeatureCombi4b(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_1_C4b, merkmalsOrganisation);

            /*
             * Combi3 (entkopplung der Nachbarfelder) ist bereits eine Katastrophe.
             * Weitere Entkopplungen brauchen nicht mehr getestet werden.
             */

            //KonvertiereKarte("hexMap14x16-02");

            //var kachelOrganisation = new LandschaftsTiling(1, 0.1); // 1 Tiling, 9 Tiles
            //IFeatures<UmgebungC> merkmalsOrganisation = new FeatureCombi1(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-02c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-02c-C1.csv");
            //merkmalsOrganisation = new FeatureCombi4(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-02c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-02c-C4.csv");
            //merkmalsOrganisation = new FeatureCombi4b(kachelOrganisation);
            //ZustandsnummernSpeichern("hexMap14x16-02c", new HexKoordinate(3, 8), merkmalsOrganisation, "zIDshexMap14x16-02c-C4b.csv");

            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-02.csv", @"Karten\zIDshexMap14x16-02c-C1.csv", "Sollwerte14x16-02c-C1.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-02.csv", @"Karten\zIDshexMap14x16-02c-C4.csv", "Sollwerte14x16-02c-C4.csv");
            //ErzeugeSollwerteDatei(@"Karten\Sollwerte14x16-02.csv", @"Karten\zIDshexMap14x16-02c-C4b.csv", "Sollwerte14x16-02c-C4b.csv");

            //var kachelOrganisation = new LandschaftsTiling(1, 0.1); // 1 Tiling, 9 Tiles
            //IFeatures<UmgebungC> merkmalsOrganisation = new FeatureCombi1(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_2_C1, merkmalsOrganisation);

            //merkmalsOrganisation = new FeatureCombi4(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_2_C4, merkmalsOrganisation);

            //merkmalsOrganisation = new FeatureCombi4b(kachelOrganisation);
            //RunTilingZTest(Experiment1.Karten.Map14x16_2_C4b, merkmalsOrganisation);

            Console.WriteLine("Taste drücken zum Beenden.");
            Console.ReadKey(true);
        }

        static void RunTilingZTest(Experiment1.Karten karte, IFeatures<UmgebungC> merkmalsOrganisation) {
            Experiment1 experiment = new Experiment1();

            bool loadSucc = experiment.InitTestUmgebung(karte, merkmalsOrganisation);

            if (!loadSucc) {
                Console.WriteLine("Error beim Laden der Testumgebung");
                Console.WriteLine("Beliebige Taste zum Beenden des Programms");
                Console.ReadKey();
            }
            bool initSucc = experiment.SetzeLernparameter(); // benutze Standardwerte
            if (!initSucc) {
                Console.WriteLine("Error beim Setzen der Lernparameter");
                Console.WriteLine("Beliebige Taste zum Beenden des Programms");
                Console.ReadKey();
                return;
            }
            Console.WriteLine("Experiment gestartet...");
            experiment.StarteExperiment(); // mit 500 episoden, 10 Durchläufen
            // StarteExperiment(episodes: 300, passes: 5)

            Console.WriteLine();
            Console.WriteLine("Experiment beendet.");
        }

        static void RunTilingXTest(Experiment1.Karten karte, LandschaftsTiling tiling) {
            Experiment1 experiment = new Experiment1();

            IFeatures<UmgebungC> merkmalsOrganisation = new FeatureCombi1(tiling);
            bool loadSucc = experiment.InitTestUmgebung(karte, merkmalsOrganisation);

            if (!loadSucc) {
                Console.WriteLine("Error beim Laden der Testumgebung");
                Console.WriteLine("Beliebige Taste zum Beenden des Programms");
                Console.ReadKey();
            }
            bool initSucc = experiment.SetzeLernparameter(); // benutze Standardwerte
            if (!initSucc) {
                Console.WriteLine("Error beim Setzen der Lernparameter");
                Console.WriteLine("Beliebige Taste zum Beenden des Programms");
                Console.ReadKey();
                return;
            }
            Console.WriteLine("Experiment gestartet...");
            experiment.StarteExperiment(); // mit 500 episoden, 10 Durchläufen
            // StarteExperiment(episodes: 300, passes: 5)

            Console.WriteLine();
            Console.WriteLine("Experiment beendet.");
        }


        static void RunTiling1Test() {
            Experiment1 experiment = new Experiment1();

            IFeatures<UmgebungC> merkmalsOrganisation = new FeatureCombi1(new LandschaftsTiling());
            bool loadSucc = experiment.InitTestUmgebung(Experiment1.Karten.Map9x10_2, merkmalsOrganisation);

            if (!loadSucc) {
                Console.WriteLine("Error beim Laden der Testumgebung");
                Console.WriteLine("Beliebige Taste zum Beenden des Programms");
                Console.ReadKey();
            }
            bool initSucc = experiment.SetzeLernparameter(); // benutze Standardwerte
            if (!initSucc) {
                Console.WriteLine("Error beim Setzen der Lernparameter");
                Console.WriteLine("Beliebige Taste zum Beenden des Programms");
                Console.ReadKey();
                return;
            }
            Console.WriteLine("Experiment " + 1 + " gestartet...");
            // Timer oder Statusanzeige
            //TimeSpan duration = new TimeSpan();
            //TimeSpan oneSec = new TimeSpan(0, 0, 1);

            //var aTimer = new System.Timers.Timer(1000);
            //aTimer.Elapsed += (sender, eArg) => {
            //    duration += oneSec;
            //    Console.Write(duration.ToString());
            //    Console.CursorLeft = 0;
            //};
            //aTimer.Start();

            Task expTask = new Task(() => experiment.StarteExperiment()); // mit 500 episoden, 10 Durchläufen
            // StarteExperiment(episodes: 300, passes: 5)

            expTask.Start();
            expTask.Wait();
            //aTimer.Stop();
            Console.WriteLine();
            Console.WriteLine("Experiment " + 1 + " beendet.");
        }


        /// <summary>
        /// Speichert die Zustandsnummern samt Feature-Nummern in Karten\zIDs"+kartenName+now.ToShortDateString()+".csv
        /// </summary>
        /// <param name="kartenName">Name of the karten.</param>
        /// <param name="ziel">The ziel.</param>
        /// <param name="tilingsOrg">Tilings Organisation</param>
        /// <param name="fileName">Dateiname (z.B. "meineZIDs.csv") der Zustandsnummern</param>
        static void ZustandsnummernSpeichern(string kartenName, HexKoordinate ziel, IFeatures<UmgebungC> tilingsOrg, string fileName=null) {
            var hexKarte = Helper.LoadMapFromFile<KachelC>(@"Karten\"+kartenName+".bin");
            KarteC karte = new KarteC(hexKarte);
            karte.KachelWertUnterteilung = tilingsOrg; // ReSpawnAgent erfolgt unten

            karte.Ziel = ziel; //wie im Experiment. Wichtig, da Richtung und Entfernung zum Zustand gehören.

            UmgebungC sichtFeld = null;
            HexRichtungen richtung;
            int zID;
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Koordinate;Merkmal 0-n;Hashed ID");

            HexKoordinate position = new HexKoordinate();
            HexKoordinate nextPos = new HexKoordinate();
            for (int row = 1; row <= hexKarte.Height-2; row++) {
                position = new HexKoordinate(row, 0);
                for (int col = 0; col <= hexKarte.Width-3; col++) {
                    richtung = (col % 2) == 0 ? HexRichtungen.SO : HexRichtungen.NO;
                    nextPos = HexMapRectFlat.Move(position, richtung);
                    if (hexKarte[nextPos].Content.Typ != Kacheltyp.Wasser) {
                        sichtFeld = karte.ReSpawnAgent(nextPos);
                        int fID = sichtFeld.FeaturedID;
                        int[] fSet = sichtFeld.GetFeatures().ToArray();
                        zID = fSet[0];//AgentC.GibZustandsIndex(sichtFeld);
                        sb.AppendFormat("#({0,2}, {1})", karte._aktuellePosition.Row, karte._aktuellePosition.Col);
                        foreach (var fNum in fSet) {
                            sb.AppendFormat(";{0,8}", fNum);
                        }
                        sb.AppendFormat(";{0,10}", fID);
                        if (ziel == karte._aktuellePosition)
                            sb.Append(";Ziel");
                        sb.AppendLine();
                    }
                    position = nextPos;
                }
            }
            SafeZustandsIDs(sb.ToString(), kartenName, fileName);
        }

        static void SafeZustandsIDs(string text, string kartenName, string fileName=null) {
            if (fileName == null) {
                DateTime now = DateTime.Now;
                fileName = "zIDs" + kartenName+now.ToShortDateString() + ".csv";
            }
            using (StreamWriter sw = File.CreateText(@"Karten\"+fileName)) {
                sw.Write(text);
            }
        }

        static void KonvertiereKarte(string kartenName) {
            string path = @"Karten\"+kartenName+".bin";
            HexMapRectFlat<Bettzueche.Dampfross.Kachel> mapDiskret = Helper.LoadMapFromFile<Bettzueche.Dampfross.Kachel>(path);
            //HexMapRectFlat<Bettzueche.Dampfross.Types.Kachel> newMap = Helper.ConvertOldKachel(mapDiskret);
            HexMapRectFlat<KachelC> mapContin = Helper.ConvertToContinuum(mapDiskret);
            Helper.SafeMapToFile<KachelC>(@"Karten\"+kartenName+"c.bin", mapContin);
        }

        static void ErzeugeSollwerteDatei(string originalSoll, string zIDs, string sollNeu) {
            StringBuilder sb = new StringBuilder("nr;id;NO;N;NW;SW;S;SO\r\n");
            string sollWerte = "";
            try {
                //var idLines = File.ReadLines(filename);
                sollWerte = File.ReadAllText(originalSoll);
            }
            catch (Exception e) {
                Debug.WriteLine("LadeSollwerte: Failed to read file. Reason: " + e.Message);
                return;
            }
            //0;44404794;19;19;19;19;20;17;
            Regex rgx1 = new Regex("^(\\d+;)\\s*(\\d+)(;.+)\\r$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
            var matches = rgx1.Matches(sollWerte);
            int[] ids = Helper.LadeFeatureIDs(zIDs);

            if (matches.Count > 0) {
                // Count ==? ids.Length
                for (int i = 0; i < matches.Count; i++) {
                    Match match = matches[i];
                    string newLine = match.Groups[1].Value + ids[i] + match.Groups[3].Value;
                    sb.AppendLine(newLine);
                }
            }

            using (StreamWriter sw = File.CreateText(@"Karten\"+sollNeu)) {
                sw.Write(sb.ToString());
            }
        }


    }
}
